#include "test.hpp"

#include "Core/shared_ptr.hpp"
using namespace lib2Desa;

static int num_obj = 0;

class A
{
	public:

		A(void)
		{
			num_obj++;
		}

		~A(void)
		{
			num_obj--;
		}

		bool operator==(const A& o) const
		{
			return (this == &o);
		}

		int foo(void) const
		{
			return 1234;
		}
};

class B : public A
{
	public:

		B(void)
		{
			num_obj++;
		}

		~B(void)
		{
			num_obj--;
		}
};

int main(int argc, char** args)
{
	BEGIN_TEST_CASE();

	// TESTING shared_ptr

	shared_ptr<A> p;
	shared_ptr<char> p2;

	ASSERT("pointer not null", !p);
	ASSERT("pointer not null", !((A*)p));
	ASSERT("pointer not null", !p2);
	ASSERT("pointer not null", !((char*)p2));

	p = new A;
	p2 = new char;

	ASSERT("pointer null", p);
	ASSERT("pointer null", ((A*)p));
	ASSERT("pointer null", p2);
	ASSERT("pointer null", ((char*)p2));

	p = 0;
	p2 = 0;

	ASSERT("pointers should be different", !(p == p2));
	ASSERT("pointers should be different", !(p2 == p));
	ASSERT("pointers should be equal", (p == p));
	ASSERT("pointers should be equal", (p2 == p2));

	shared_ptr<A> p3 = p;

	ASSERT("pointers should be equal", (p3 == p3));
	ASSERT("pointers should not be equal", !(p == p3));
	ASSERT("pointers should not be equal", !(p3 == p));
	ASSERT("pointers should be equal", ( ((A*)p3) == ((A*)p) ));

	ASSERT("number of objects wrong", (num_obj == 0));

	shared_ptr<A> p4 = new A;

	ASSERT("number of objects wrong", (num_obj == 1));

	p4 = 0;

	ASSERT("number of objects wrong", (num_obj == 0));

	p4 = new A;

	ASSERT("number of objects wrong", (num_obj == 1));

	p4 = p;

	ASSERT("number of objects wrong", (num_obj == 0));

	shared_ptr<B> p5 = new B;

	ASSERT("number of objects wrong", (num_obj == 2));

	p4 = p5;

	ASSERT("number of objects wrong", (num_obj == 2));
	ASSERT("pointers should be equal", ( ((A*)p5) == ((A*)p4) ));
	ASSERT("pointers should be equal", ( ((B*)p5) == ((A*)p4) ));

	p4 = 0;

	ASSERT("number of objects wrong", (num_obj == 2));

	p5 = 0;

	ASSERT("number of objects wrong", (num_obj == 0));

	p = new A;

	ASSERT("number of objects wrong", (num_obj == 1));

	p3 = p;
	p4 = p3;

	ASSERT("number of objects wrong", (num_obj == 1));

	p = 0;
	p3 = 0;

	ASSERT("number of objects wrong", (num_obj == 1));

	p4 = 0;

	ASSERT("number of objects wrong", (num_obj == 0));

	p = new A;

	ASSERT("function call unsuccessful", (p->foo() == 1234));

	p = 0;

	// TESTING weak_ptr

	weak_ptr<A> w1;

	ASSERT("pointer not null", !w1);
	ASSERT("pointer not null", !((A*)w1));

	p = new A;
	w1 = p;

	ASSERT("pointer null", w1);
	ASSERT("pointer null", ((A*)w1));
	ASSERT("number of objects wrong", (num_obj == 1));

	p = 0;

	ASSERT("pointer not null", !w1);
	ASSERT("pointer not null", !((A*)w1));
	ASSERT("number of objects wrong", (num_obj == 0));

	p = new A;
	weak_ptr<A> w2 = p;

	ASSERT("pointer null", w2);
	ASSERT("pointer null", ((A*)w2));
	ASSERT("number of objects wrong", (num_obj == 1));
	ASSERT("function call unsuccessful", (w2->foo() == 1234));

	w1 = w2;

	ASSERT("pointer null", w1);
	ASSERT("pointer null", ((A*)w1));
	ASSERT("number of objects wrong", (num_obj == 1));
	ASSERT("function call unsuccessful", (w1->foo() == 1234));
	ASSERT("pointers should be equal", (w1 == w2));
	ASSERT("pointers should be equal", ( ((A*)w1) == ((A*)w2) ));

	w2 = 0;

	ASSERT("pointers should not be equal", !(w1 == w2));
	ASSERT("pointers should not be equal", !( ((A*)w1) == ((A*)w2) ));

	w2 = w1;

	END_TEST_CASE();
}
